#include <iostream>
#include "Estadistico.h"
#include "Fifo.h"
#include "Canales.h"
#include "Logger.h"
#include "FuncionesAuxiliares.h"
#include "Aviso.h"
#include "SignalHandler.h"

#define TAMBUFFER 100
#define MAXLETRAS 7 //letras de chancho

using namespace std;

Estadistico::Estadistico(int cantidadJugadores) {
	// Registro de señales.
	SignalHandler* sh = SignalHandler :: getInstance();
	sh->registrarHandler ( SIGINT, &sigint_handler );
	sh->registrarHandler ( SIGUSR1, &sigusr_handler );	
	
	// Creación de canales.
	cantJugadores = cantidadJugadores;
	aPerdedores = new Fifo(ESTADPERDEDOR);
	dePerdedores = new Fifo(PERDEDORESTAD);
	aCoord = new Fifo(ESTADCOORD);
	deCoord = new Fifo(COORDESTAD);
	aPerdSinc = new Fifo(ESTADPERDSINC);
	puntaje = map<string,int>();
	for (int i = 1; i <= cantJugadores; i++)
		puntaje[ itos(i) ] = 0;
}


Estadistico::~Estadistico() {
	delete aPerdedores;
	delete dePerdedores;
	delete aCoord;
	delete deCoord;
	delete aPerdSinc;
	SignalHandler::destruir();
}


int Estadistico::correr() {
	// Sincronización.
	recibirOKPerdedores();
	Logger::loggear("Recibe OK perdedores","ESTADISTICO");
	
	recibirAviso(deCoord);
	Logger::loggear("Recibe OK del Coordinador","ESTADISTICO");
	
	// En este punto el coordinador ya puede arrancar (todos los jugadores y los
	// perdedores le dieron el OK.
	while ( !terminoJuego() && !sigint_handler.getGracefulQuit() ) {
		Logger::loggear("Avisa al Coordinador que inicie mano","ESTADISTICO");
		avisar(aCoord, OK);
		// Espera que le avisen quién ganó.
		Logger::loggear("Espera recibir ganador","ESTADISTICO");
		int ganador = recibirAviso(deCoord).getTipo();
		Logger::loggear("Obtiene Ganador","ESTADISTICO");
		cout << "ESTADÍSTICO: recibi ganador: "<< ganador << endl;
		
		// Avisa para que apoyen la mano.
		avisarPerdedores(aPerdedores, FINDERONDA);
		Logger::loggear("Avisa Perdedores que apoyen la mano","ESTADISTICO");
		int perdedor = obtenerPerdedor(ganador);
		Logger::loggear("Obtiene Perdedor","ESTADISTICO");
		actualizarPuntaje(perdedor);
		cout << "ESTADÍSTICO: recibi perdedor: "<<perdedor << endl;
		
		/* Para poder probar la señal.
		sleep(1); */

		// Le avisa a los perdedores del inicio de una nueva ronda.
		avisarPerdedores(aPerdSinc, OK);
		Logger::loggear("Avisa Perdedores de nueva ronda","ESTADISTICO");
		
		// Manejo de señal SIGUSR1.
		if(sigusr_handler.getGracefulQuit() != 0)
		{
			mostrarPuntaje();
			sigusr_handler.restaurarEstado();
		}
	}
	mostrarPuntaje();
	Logger::loggear("Avisa a Coordinador de FINDEJUEGO","ESTADISTICO");
	avisar(aCoord, FINDELJUEGO);
	
	avisarPerdedores(aPerdedores, FINDELJUEGO);
	cerrarCanales();
	Logger::loggear("Finaliza","ESTADISTICO");
	return 0;
}


void Estadistico::cerrarCanales() {
	aPerdedores->cerrar();
	aPerdedores->eliminar();
	dePerdedores->cerrar();
	dePerdedores->eliminar();
	aCoord->cerrar();
	aCoord->eliminar();
	deCoord->cerrar();	
	aPerdSinc->cerrar();
	aPerdSinc->eliminar();
}


int Estadistico::terminoJuego() {
	for (map<string,int>::iterator it = puntaje.begin(); it != puntaje.end(); it++)
		if (it->second == MAXLETRAS) return 1;
	return 0;
} 


void Estadistico::actualizarPuntaje(int perdedor) {
	string nombrePerdedor = itos(perdedor);
	puntaje[nombrePerdedor] += 1;
}


void Estadistico::mostrarPuntaje() {
	cout << "Puntajes:" << endl;
	string chancho[] = { "C", "H", "A", "N", "C", "H", "O" };
	for (map<string,int>::iterator it = puntaje.begin(); it != puntaje.end(); it++) {
		int cantPerdidas = it->second;
		string pal("");
		for (int i = 0; i < cantPerdidas; i++) pal += chancho[i];
		cout << "Jugador " << it->first <<": "<< pal << endl;
	}
}
	
	
void Estadistico::avisarPerdedores(Comunicador *canal, int tipoAviso) {
	for (int i = 0; i < cantJugadores; i++)
		avisar(canal, tipoAviso);
}


int Estadistico::obtenerPerdedor(int ganador) {
	// Los primeros cantJugadores - 2 se descartan.
	for (int i = 0; i < cantJugadores - 2; i++)
		recibirAviso(dePerdedores);
	int anteultimo = recibirAviso(dePerdedores).getTipo();
	int ultimo = recibirAviso(dePerdedores).getTipo();
	if (ultimo == ganador) return anteultimo;
	return ultimo;
}


void Estadistico::recibirOKPerdedores() {
	for (int i = 0; i < cantJugadores; i++)
		recibirAviso(dePerdedores);
}
